
package bebLogical;

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Panel;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Stack;
import javax.swing.JOptionPane;

import bebGraphical.*;
import bebDialogs.*;

	public class BebMain extends Applet implements MouseListener,ActionListener,AdjustmentListener,ItemListener{

		
		
		
		Panel buttonsHandler=new Panel();
		Panel stationsHandler=new Panel();
		Panel slotsHandler=new Panel();
	   
		ScrollPane slotsHandlerScroller=new ScrollPane(); 
		
		Button addStation=new Button("Add new station");
		Button rmStation=new Button("Remove station");
		Button previous=new Button("Previous slot");
		Button next=new Button("Next slot");
		Button start=new Button("Start");
		Button pause=new Button("Pause / Resume");
		Button abort=new Button("Abort session");
		Button erase=new Button("Erase all slots");

		Choice interSendingLengthChoice=new Choice();
		Choice packetLengthChoice=new Choice();
		Choice collisionLengthChoice=new Choice();
		Scrollbar simulationSpeedScroller=new Scrollbar(Scrollbar.HORIZONTAL, 200, 1, 0, 255);
		
		
		int nbrOrdiMax=20;
		
		
		Stack<Slot> slotList=new Stack<Slot>();
		Stack<Ordi> ordiList=new Stack<Ordi>();
		Stack<Ordi> tmpOrdiList=new Stack<Ordi>();
			
		Simula simula=new Simula();
		
		/*
		 * en haut les attributs 
		 * en bas les m�thodes 
		 */
		private static final long serialVersionUID = 1L;
		
		@Override
		public void init() {
			
			
			this.setLayout(new BorderLayout());
			this.add(buttonsHandler,BorderLayout.WEST);
			this.add(stationsHandler,BorderLayout.CENTER);
			this.add(slotsHandlerScroller,BorderLayout.SOUTH);
			
			buttonsHandler.setBackground(new Color(255, 255, 128));
			buttonsHandler.setLayout(new GridBagLayout());
			
			GridBagConstraints c=new GridBagConstraints();
			c.anchor=GridBagConstraints.LINE_START;
			c.gridwidth=2;
			c.gridy=0;
			c.insets=new Insets(5, 5, 5, 5);
			
		buttonsHandler.add(addStation,c);
		c.gridy++;
		buttonsHandler.add(rmStation,c);
		c.gridy++;
		buttonsHandler.add(start,c);
		c.gridy++;
		buttonsHandler.add(pause,c);
		c.gridy++;
		buttonsHandler.add(abort,c);
		c.gridy++;
		buttonsHandler.add(erase,c);
		c.gridy++;
		buttonsHandler.add(previous,c);
		c.gridy++;
		buttonsHandler.add(next,c);
		c.gridy++;
		c.gridwidth=1;
		
		Label l=new Label("Packet length");
		l.setBackground(Color.white);
		
		buttonsHandler.add(l,c);
		c.gridx=1;
		buttonsHandler.add(packetLengthChoice,c);
		c.gridy++;
		c.gridx=0;
		
		l=new Label("Collision length");
		l.setBackground(Color.white);
		
		buttonsHandler.add(l,c);
		c.gridx=1;
		buttonsHandler.add(collisionLengthChoice,c);
		c.gridy++;
		c.gridx=0;
		
		l=new Label("Gap length");
		l.setBackground(Color.white);
		
		buttonsHandler.add(l,c);
		c.gridx=1;
		buttonsHandler.add(interSendingLengthChoice,c);
		c.gridy++;
		c.gridx=0;
		c.gridwidth=2;
		
		l=new Label("Simulation speed");
		l.setBackground(Color.white);
		
		buttonsHandler.add(l,c);
		c.gridy++;
		buttonsHandler.add(simulationSpeedScroller,c);
			
			
		Dimension ps=new Dimension(150,25);

		simulationSpeedScroller.setPreferredSize(new Dimension(150,15));
		addStation.setPreferredSize(ps);
		rmStation.setPreferredSize(ps);
		previous.setPreferredSize(ps);
		next.setPreferredSize(ps);
		start.setPreferredSize(ps);
		pause.setPreferredSize(ps);
		abort.setPreferredSize(ps);
		erase.setPreferredSize(ps);
		
		
		
		simulationSpeedScroller.setBackground(Color.white);

			for(int i=1;i<=9;i++)
			packetLengthChoice.addItem(""+i);
			
			for(int i=1;i<=9;i++)
			collisionLengthChoice.addItem(""+i);
				
			for(int i=0;i<=5;i++)
			interSendingLengthChoice.addItem(""+i);
			
			
		slotsHandlerScroller.add(slotsHandler);

		}
		
		public BebMain() {
			previous.addActionListener(this);
			next.addActionListener(this);
			addStation.addActionListener(this);
			rmStation.addActionListener(this);
			start.addActionListener(this);
			pause.addActionListener(this);
			abort.addActionListener(this);
			erase.addActionListener(this);
			simulationSpeedScroller.addAdjustmentListener(this);
			packetLengthChoice.addItemListener(this);
			collisionLengthChoice.addItemListener(this);
			interSendingLengthChoice.addItemListener(this);
			
		}
		
		/*
		 * cette m�thode permet de tester si les ordinateurs 
		 * veulent encore envoyer ou nn
		 * 
		 */
		boolean noneWantsToSend(){
			for(int i=0;i<ordiList.size()&& !ordiList.isEmpty();i++){
				if(ordiList.elementAt(i).getRemainingSlots()>0)
					return false ;
			}
			return true;
		}
		class Simula extends Thread{
			volatile Thread beb=null;
			int sleepingInterval=0;
			boolean suspended=false;
			
			/*
			 * des attributs utiles pour le gestion 
			 * des messages qui s'�tendent sur plusieurs slots 
			 * 
			 */
			
			/*
			 * pc comme pack count
			 * cC comme collision count
			 * 
			 * */
			
			/*
			 * leurs incr�mentation est modulo Ordi.getPackLength() pour pc
			 * et Ordi.getCollisionLength() pour cc
			 * 
			 * */
			
			
			int pc=0;
			int cc=0;
			
			
			public void start() {
				if (beb==null) {
					
					/*
					 * ce test permet d'�viter l'effet 
					 * des clics non abusif sur le bouton 
					 * pause
					 * 
					 */
					if (suspended) {

						synchronized (this) {
							suspended = false;
							notify();
						}
					}
					
					beb = new Thread(this);
					beb.start();
				}else;
		    }
			
		    public synchronized void  stop_2(){
		    	beb=null;
		    	synchronized (this) {
					notify();
				}
		    }

			public boolean isSuspended() {
				return suspended;
			}

			public void setSuspended(boolean suspended) {
				this.suspended = suspended;
			}
			
			public void setSleepingInterval(int sleepingInterval) {
				this.sleepingInterval = (sleepingInterval+1)*15;
			}
			
			public void run() {
				Thread thisThread = Thread.currentThread();
				
		        
		        while (beb == thisThread) {
		        	
		        	
		            Slot slot=new Slot();
		    		slot.addMouseListener(BebMain.this);
		            
		            /*
		             * au d�but de chaque p�riode il est n�cessaire de prendre des snapshots 
		             * afin de les exploiter pour l'historique  
		             * 
		             */
		            
		            
		            
	/*
	 * 
	 * ajouter des ordinateurs � la liste des ordi tmp qui , 
	 * si sa taille est �gale � un alors une station va envoyer sinn 
	 * si sa taille est sup strict � un alors il y a collision 
	 * sinn si sa taille est nulle alors aucune station n'envoie 
	 * 
	 * */
		            /*
		             * 
		             * on d�cr�mente le backoff des stations 
		             * */
		            
		            for(int i=0;i<ordiList.size();i++){
						ordiList.elementAt(i).decrementBackOff();
					}
		            
		            /*
		             * au d�but il faut vider la liste tmp des ordis
		             * 
		             * */
		            
		            /*
		             * RQ: la liste ne se vide que lorsque les deux compteurs sont 
		             * nuls ! ces valeurs nuls indiquent qu'il n y a pas d'envoie en cours  
		             * ni de collision en cours 
		             * 
		             * */
		            
		            if(!tmpOrdiList.isEmpty()&&cc==0&&pc==0){
		            	if(tmpOrdiList.size()==1&&tmpOrdiList.lastElement().getRemainingSlots()>0){
		            		tmpOrdiList.lastElement().incrementBackOffUrgentCase(Ordi.getInterLength());
		            	}
		            	
		            	tmpOrdiList.clear();
		            	
		            }
		            
		            /*
		             * puis voir si la liste d'ordis contient des messges � �mettre 
		             * dans ce cas on les ajoute � la liste tmp
		             * */
		            
		            for (int i=0;i<ordiList.size();i++){
		            	
		            	Ordi ordi =ordiList.elementAt(i);
		            	
		            	if(ordi.getBo()==0&&ordi.getRemainingSlots()>0){
		            		/* le fait que le back off est nul et le nombre de packets est sup�rieur � 0
		            		 * n'impliquent pas directement que la station doit etre empilee
		            		 *  
		            		 */
		            		
		            		if(cc==0&&pc==0){
		            			/*
		            			 * soit on est au d�but d'un front 
		            			 * donc on renouvelle le conrenu de la liste 
		            			 * temporaire des ordinateurs en collision ou de l'ordinateur 
		            			 * qui va effectuer l'envoie 
		            			 * 
		            			 */
		            			
		            		tmpOrdiList.push(ordi);
		            		}else{
		            			/*
		            			 * soit non (i.e) qu'on est en train d'effectuer 
		            			 * l'envoie d'une portion d'un message sur un slot 
		            			 * bien d�termin� 
		            			 * ceci implique que tout les ordinateurs sauf ceux dans la liste 
		            			 * doivent incr�menter leurs back off 
		            			 * 
		            			 */
		            			if( tmpOrdiList.contains(ordi)){
		            				/*
		            				 * ne rien faire si l'ordinateur est d�ja dans la liste 	
		            				 * temporaire 
		            				 */
		            				
		            			}else {
		            				ordi.incrementBackOffUrgentCase
		            				(tmpOrdiList.lastElement().getRemainingSlots()%Ordi.getPackLength());
		            			}
		            			
		            		}
		            		
		            	}else;
		            }
		            
		            /*
		             * par la suite on teste sur cette mm liste 
		             * 
		             */
		            
		            if(tmpOrdiList.size()==1){
		            Ordi ordi= tmpOrdiList.lastElement();
		            slot.setUsedBy(ordi.getoName());
		            ordi.decrementRemainingSlots();
		            ordi.setSending(true);
		            
		            /*
		             * � ce point on vient d'envoyer un slot donc une partie du message
		             * ceci implique qu'on doit incr�menter le pc
		             * 
		             */
		            pc++;
		            pc%=Ordi.getPackLength();
		            }else if(tmpOrdiList.size()>1){
		            	slot.setUsedBy("X");
		            	for(int i=0;i<tmpOrdiList.size();i++){
		            		tmpOrdiList.elementAt(i).setCrash(true);
		            		
		            	}
		            /*
		             * � ce point on vient de mentionner une pqrtie de la  collision
		             * donc on doit incr�menter cc 	
		             */
		            	
		            cc++;
		            cc%=Ordi.getCollisionLength();
		            }else{
		            	slot.setUsedBy("--");
		            }
		            
		            /*
		             * � ce stade on enregistre des points de restoration 
		             * sur tout les PC du r�seau 
		             */
		            
		            
		            slotList.push(slot);
		    		slotsHandler.add(slot);
		    		slotsHandler.doLayout();
					slotsHandlerScroller.doLayout();
					slotsHandlerScroller.setScrollPosition(slotsHandler.getWidth(), 60);

					
					
					try {
		                Thread.sleep(sleepingInterval);           
		            } catch (InterruptedException e){
						e.printStackTrace();
		            }
					
					/*
					 * cr�ation des points de restoration
					 */
					
					for(int i=0;i<ordiList.size();i++){
		            	ordiList.elementAt(i).createRestorationPoint(slot.getSlotNumber());
		            	
					}
					
					
					/*
					 * on enl�ve les deux barres rouges qui marquent 
					 * la collision 
					 * 
					 */
					
					for(int i=0;i<tmpOrdiList.size()&&cc==0;i++){
						if(tmpOrdiList.size()>1){
						tmpOrdiList.elementAt(i).setCrash(false);
						tmpOrdiList.elementAt(i).onCrashConfig();	
						}else {
						tmpOrdiList.elementAt(i).setSending(false);	
						}
	            		
	            	}
					
					if(ordiList.isEmpty() || noneWantsToSend()){
						stop_2();
						Slot.setOnClicRestore(true);
						next.setEnabled(true);
						previous.setEnabled(true);
						break;
						
					}
					
		           
					synchronized (this) {
						while(suspended&&beb == thisThread){
							try {
								wait();
							} catch (InterruptedException e) {
								e.printStackTrace();}}}
					
		        }/*end while */
		    }
		  }
		
		
		private void prevAction() {
			if(Slot.getSelectedSlot()==-1){
				Warning.warningInformation( "Please select a slot (by a simple click)","Abuse");	
				}else {
	
					int oldSectedSlot=Slot.getSelectedSlot();
					Slot.decrementSelectedSlot();
					
					/*
					 * pui in redessine l'ancien slot qui ne sera plus s�lectionn� 
					 */
					if (0 <= oldSectedSlot && slotList.size() > oldSectedSlot) {
						slotList.elementAt(oldSectedSlot).repaint();
					} else;
					
					/*
					 * enfin on redessine le slot courant
					 */
					int newSectedSlot=Slot.getSelectedSlot();
					slotList.elementAt(newSectedSlot).repaint();	
					
				
				for(int i=0;i<ordiList.size();i++){
				ordiList.elementAt(i).applyRestorationPoint(newSectedSlot);	
				}
				
				}
		}
		
		private void nextAction() {
			if(Slot.getSelectedSlot()==-1){
				Warning.warningInformation( "Please select a slot (by a simple click)","Abuse");	
				}else {
				
					int oldSectedSlot=Slot.getSelectedSlot();
					Slot.incrementSelectedSlot();
					
					/*
					 * pui in redessine l'ancien slot qui ne sera plus s�lectionn� 
					 */
					if (0 <= oldSectedSlot && slotList.size() > oldSectedSlot) {
						slotList.elementAt(oldSectedSlot).repaint();
					} else;
					
					/*
					 * enfin on redessine le slot courant
					 */
					int newSectedSlot=Slot.getSelectedSlot();
					slotList.elementAt(newSectedSlot).repaint();	
					
				
				for(int i=0;i<ordiList.size();i++){
				ordiList.elementAt(i).applyRestorationPoint(newSectedSlot);	
				}	
				
				}
		}
		
		@Override
		public void itemStateChanged(ItemEvent a) {
			Ordi.setPackLength(packetLengthChoice.getSelectedIndex());
			Ordi.setInterLength(interSendingLengthChoice.getSelectedIndex());
			Ordi.setCollisionLength(collisionLengthChoice.getSelectedIndex());
		}

		@Override
		public void adjustmentValueChanged(AdjustmentEvent arg0) {
			simula.setSleepingInterval(255-simulationSpeedScroller.getValue());
		}

		@Override
		public void actionPerformed(ActionEvent a) {
			Object bs= a.getSource();
			if(bs==previous){
				prevAction();
			}else if(bs==next){
				nextAction();
			}else if(bs==addStation){
				if(ordiList.size()<nbrOrdiMax){
					Ordi ordi=new Ordi();
					ordiList.push(ordi);
					
					stationsHandler.add(ordi);
					
					stationsHandler.doLayout();
					
					ordi.doLayout();
					
					interSendingLengthChoice.setEnabled(false);
					collisionLengthChoice.setEnabled(false);
					packetLengthChoice.setEnabled(false);
					}
			}else if(bs==rmStation){
				if(!ordiList.isEmpty()){
					Ordi ordi=ordiList.pop();
					stationsHandler.remove(ordi);
					stationsHandler.doLayout();
					Ordi.delete();
					}else;
	
				/*
				 * si tout les stations ont �t� supprim�s 
				 * et si de plus la liste de slots est vide 
				 * il est convenable de r�activer les "Choices"
				 *  
				 */
				if(ordiList.isEmpty()&&slotList.isEmpty()){
					collisionLengthChoice.setEnabled(true);
					packetLengthChoice.setEnabled(true);
				}
			}else if(bs==start){
				if(ordiList.isEmpty() || noneWantsToSend()){
					if(Slot.isOnClicRestore()){
						Warning.warningInformation("Session ended\nAbort or Erase current session to begin a new one\notherwise you can use Next and Previous buttons to browse ended session","Abuse");
					}else {
						Warning.warningInformation("Please add some stations, \nif you have already added some, \nPlease add some Packets","404");
					}
					
					
				}else {
				simula.setSleepingInterval(255-simulationSpeedScroller.getValue());
				collisionLengthChoice.setEnabled(false);
				packetLengthChoice.setEnabled(false);
				interSendingLengthChoice.setEnabled(false);
				
				addStation.setEnabled(false);
				rmStation.setEnabled(false);
				next.setEnabled(false);
				previous.setEnabled(false);
				
				/*
				 * � ce stade il faut 
				 * interdire tt changement sur le textfield
				 */
				for(int i=0;i<ordiList.size();i++){
				ordiList.elementAt(i).setEditable(false);	
				}
			
				simula.start();
				}
			}else if(bs==pause){
				synchronized (simula) {
					simula.notify();
					simula.setSuspended(!simula.isSuspended());
				}
			}else if(bs==abort){
				int confirmation = Warning.warningConfirmation("Are you sure, \nthis action will abort, \nthe whole session","confirm");
				
				if (confirmation==JOptionPane.YES_OPTION) {
					simula.stop_2();
					Slot.resetParam();
					Slot.setOnClicRestore(false);
					Ordi.resetParam();
					for (int i = 0; i < ordiList.size(); i++) {
						Ordi ordi = ordiList.elementAt(i);

						stationsHandler.remove(ordi);
					}
					ordiList.removeAll(ordiList);
					for (int i = 0; i < slotList.size(); i++) {
						Slot slot = slotList.elementAt(i);

						slotsHandler.remove(slot);
					}
					slotList.removeAll(slotList);
					collisionLengthChoice.setEnabled(true);
					packetLengthChoice.setEnabled(true);
					interSendingLengthChoice.setEnabled(true);
					addStation.setEnabled(true);
					rmStation.setEnabled(true);
					next.setEnabled(true);
					previous.setEnabled(true);
					Slot.setSelectedSlot(-1);
				}else ;

			}else if(bs==erase){
				int confirmation=Warning.warningConfirmation( "Are you sure, \nthis action will erase, \nall slot list","confirm");
				
				if (confirmation==JOptionPane.YES_OPTION) {
					simula.stop_2();
					Slot.resetParam();
					Slot.setOnClicRestore(false);
					for (int i = 0; i < ordiList.size(); i++) {
						ordiList.elementAt(i).onEraseConfig();
						ordiList.elementAt(i).setEditable(true);
					}
					for (int i = 0; i < slotList.size(); i++) {
						Slot slot = slotList.elementAt(i);
						slotsHandler.remove(slot);
					}
					slotList.removeAll(slotList);
					collisionLengthChoice.setEnabled(true);
					packetLengthChoice.setEnabled(true);
					interSendingLengthChoice.setEnabled(true);
					interSendingLengthChoice.setEnabled(true);
					addStation.setEnabled(true);
					rmStation.setEnabled(true);
					Slot.setSelectedSlot(-1);
				}else;


			}else ;
			
		}

		@Override
		public void mouseClicked(MouseEvent a) {
			// TODO Auto-generated method stub
			Slot slot=(Slot) a.getSource();
			/*
			 * on r�cup�re le num�ro du slot s�lectionn� 
			 */
			int oldSectedSlot=Slot.getSelectedSlot();
			/*
			 * on met � jour le num�ro du slot s�lectionn� 
			 */
			
			int newSectedSlot=slot.getSlotNumber();
			/*
			 * on ajuste la variable statique selectedSlot
			 */
			Slot.setSelectedSlot(newSectedSlot);
			
			/*
			 * pui in redessine l'ancien slot qui ne sera plus s�lectionn� 
			 */
			if (0 <= oldSectedSlot && slotList.size() > oldSectedSlot) {
				slotList.elementAt(oldSectedSlot).repaint();
			} else;
			
			/*
			 * enfin on redessine le slot courant
			 */
			
			slotList.elementAt(newSectedSlot).repaint();

			if (!slotList.isEmpty() && 0 <= newSectedSlot && slotList.size() > newSectedSlot) {
				slotList.elementAt(newSectedSlot).repaint();
			} else;
			
			for(int i=0;i<ordiList.size()&&Slot.isOnClicRestore();i++){
				ordiList.elementAt(i).applyRestorationPoint(newSectedSlot);
			}
		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mousePressed(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		
}

